import {fromJS, List, Map} from 'immutable';


export const INITIAL_STATE = Map();

export function setFolderTree(state, folderTree) {
    const tree = fromJS(folderTree);
    return state.set('folderTree', tree)
}

export function setFolderChildren(folderTreeState, folderId, newFolderChildren){
    if (!List.isList(newFolderChildren)){
        throw "newFolderChildren is invalidate!";
    }
    var keyPath = getKeyPath(folderTreeState, folderId);
    if (!keyPath) {
        throw "nodeData is invalidate!";
    }
    return folderTreeState.updateIn(keyPath, folder => folder.update("children", List(), folderChildren => {
        var newChildrenList = [];        
        newFolderChildren.forEach((newChildren, index)=>{
            const oldChildren = folderChildren.find(children => newChildren.get("id") == children.get("id"));
            if (oldChildren){
                newChildrenList.push(oldChildren.merge(newChildren));
            }
            else{
                newChildrenList.push(newChildren);
            }
        })
        return fromJS(newChildrenList);
    }));
}

export function getFolderPath(folderTreeState, folderId) {
    var path = [];
    const curFolder = getFolderById(folderTreeState, folderId);    
    path.push(curFolder);
    var parent = getParent(folderTreeState, curFolder.get("id"));
    while (parent) {
        path.push(parent);
        parent = getParent(folderTreeState, parent.get("id"));
    }
    return fromJS(path);
}

export function getBrotherFolder(folderTreeState, folderId){
    var parentFolder = getParent(folderTreeState, folderId);   
    if (parentFolder && parentFolder.get("children")){
        return parentFolder.get("children").filter(folder => (folder.get("type") == "cmis:folder") && (folder.get("id") != folderId))
    }
    return List();     
}

function getParent(folderTreeState, folderId){   
    var keyPath = getKeyPath(folderTreeState, folderId);     
    if (keyPath && keyPath.length >= 2){      
        keyPath.splice(keyPath.length-2, 2);    
        return folderTreeState.getIn(keyPath);
    }
    else{
        return null;
    }
}

function getKeyPathInChildren(parentFolder, nodeId) {
    var ret = false;
    if (parentFolder.get("children")) {
        parentFolder.get("children").forEach((folder, index) => {
            if (folder.get('id') == nodeId) {
                ret = ["children", index];
                return false;
            } else {
                var childrenPath = getKeyPathInChildren(folder, nodeId);
                if (!childrenPath) {
                    return true;
                } else {
                    ret = ["children", index].concat(childrenPath);
                    return false;
                }
            }
        })
    }
    return ret;
}

export function getKeyPath(folderTreeState, nodeId) {
    if (folderTreeState.get("id") == nodeId){
        return [];
    }
    return getKeyPathInChildren(folderTreeState, nodeId);
}

export function removeNode(folderTreeState, nodeId) {
    var keyPath = getKeyPath(folderTreeState, nodeId);
    if (!keyPath) {
        throw "nodeId is invalidate!";
    }
    return folderTreeState.deleteIn(keyPath);
}

export function addNode(folderTreeState, parentId, node) {
    var keyPath = getKeyPath(folderTreeState, parentId);
    if (!keyPath) {
        throw "parentId is invalidate!";
    }
    return folderTreeState.updateIn(keyPath, parentFolder => {
        return parentFolder.update("children", List(), folderList => folderList.push(Map(node)))
    })
}

export function getFolderById(folderTreeState, nodeId) {
    var keyPath = getKeyPath(folderTreeState, nodeId);
    if (!keyPath) {
        return null;
    }
    return folderTreeState.getIn(keyPath);
}

export function updateNode(folderTreeState, nodeId, node) {
    var keyPath = getKeyPath(folderTreeState, nodeId);
    if (!keyPath) {
        return null;
    }
    return folderTreeState.mergeIn(keyPath, node);
}


export function setEntries(state, entries) {
    const list = List(entries);
    return state.set('entries', list)
        .set('initialEntries', list);
}

function getWinners(vote) {
    if (!vote) return [];
    const [one, two] = vote.get('pair');
    const oneVotes = vote.getIn(['tally', one], 0);
    const twoVotes = vote.getIn(['tally', two], 0);
    if (oneVotes > twoVotes) return [one];
    else if (oneVotes < twoVotes) return [two];
    else return [one, two];
}

export function next(state, round = state.getIn(['vote', 'round'], 0)) {
    const entries = state.get('entries')
        .concat(getWinners(state.get('vote')));
    if (entries.size === 1) {
        return state.remove('vote')
            .remove('entries')
            .set('winner', entries.first());
    } else {
        return state.merge({
            vote: Map({
                round: round + 1,
                pair: entries.take(2)
            }),
            entries: entries.skip(2)
        });
    }
}

export function restart(state) {
    const round = state.getIn(['vote', 'round'], 0);
    return next(
        state.set('entries', state.get('initialEntries'))
        .remove('vote')
        .remove('winner'),
        round
    );
}

function removePreviousVote(voteState, voter) {
    const previousVote = voteState.getIn(['votes', voter]);
    if (previousVote) {
        return voteState.updateIn(['tally', previousVote], t => t - 1)
            .removeIn(['votes', voter]);
    } else {
        return voteState;
    }
}

function addVote(voteState, entry, voter) {
    if (voteState.get('pair').includes(entry)) {
        return voteState.updateIn(['tally', entry], 0, t => t + 1)
            .setIn(['votes', voter], entry);
    } else {
        return voteState;
    }
}

export function vote(voteState, entry, voter) {
    return addVote(
        removePreviousVote(voteState, voter),
        entry,
        voter
    );
}